jQuery.fn.extend({
    attr: function (name, value) {
        return jQuery.access(this, jQuery.attr, name, value, arguments.length > 1);
    },

    removeAttr: function (name) {
        return this.each(function () {
            jQuery.removeAttr(this, name);
        });
    },

    prop: function (name, value) {
        return jQuery.access(this, jQuery.prop, name, value, arguments.length > 1);
    },

    removeProp: function (name) {
        return this.each(function () {
            delete this[jQuery.propFix[name] || name];
        });
    },

    addClass: function (value) {
        var classes, elem, cur, clazz, j,
            i = 0,
            len = this.length,
            proceed = typeof value === "string" && value;

        if (jQuery.isFunction(value)) {
            return this.each(function (j) {
                jQuery(this).addClass(value.call(this, j, this.className));
            });
        }

        if (proceed) {
            // The disjunction here is for better compressibility (see removeClass)
            classes = (value || "").match(core_rnotwhite) || [];

            for (; i < len; i++) {
                elem = this[i];
                cur = elem.nodeType === 1 && (elem.className ?
                    (" " + elem.className + " ").replace(rclass, " ") :
                    " "
                );

                if (cur) {
                    j = 0;
                    while ((clazz = classes[j++])) {
                        if (cur.indexOf(" " + clazz + " ") < 0) {
                            cur += clazz + " ";
                        }
                    }
                    elem.className = jQuery.trim(cur);

                }
            }
        }

        return this;
    },

    removeClass: function (value) {
        var classes, elem, cur, clazz, j,
            i = 0,
            len = this.length,
            proceed = arguments.length === 0 || typeof value === "string" && value;

        if (jQuery.isFunction(value)) {
            return this.each(function (j) {
                jQuery(this).removeClass(value.call(this, j, this.className));
            });
        }
        if (proceed) {
            classes = (value || "").match(core_rnotwhite) || [];

            for (; i < len; i++) {
                elem = this[i];
                // This expression is here for better compressibility (see addClass)
                cur = elem.nodeType === 1 && (elem.className ?
                    (" " + elem.className + " ").replace(rclass, " ") :
                    ""
                );

                if (cur) {
                    j = 0;
                    while ((clazz = classes[j++])) {
                        // Remove *all* instances
                        while (cur.indexOf(" " + clazz + " ") >= 0) {
                            cur = cur.replace(" " + clazz + " ", " ");
                        }
                    }
                    elem.className = value ? jQuery.trim(cur) : "";
                }
            }
        }

        return this;
    },

    toggleClass: function (value, stateVal) {
        var type = typeof value;

        if (typeof stateVal === "boolean" && type === "string") {
            return stateVal ? this.addClass(value) : this.removeClass(value);
        }

        if (jQuery.isFunction(value)) {
            return this.each(function (i) {
                jQuery(this).toggleClass(value.call(this, i, this.className, stateVal), stateVal);
            });
        }

        return this.each(function () {
            if (type === "string") {
                // toggle individual class names
                var className,
                    i = 0,
                    self = jQuery(this),
                    classNames = value.match(core_rnotwhite) || [];

                while ((className = classNames[i++])) {
                    // check each className given, space separated list
                    if (self.hasClass(className)) {
                        self.removeClass(className);
                    } else {
                        self.addClass(className);
                    }
                }

                // Toggle whole class name
            } else if (type === core_strundefined || type === "boolean") {
                if (this.className) {
                    // store className if set
                    data_priv.set(this, "__className__", this.className);
                }

                // If the element has a class name or if we're passed "false",
                // then remove the whole classname (if there was one, the above saved it).
                // Otherwise bring back whatever was previously saved (if anything),
                // falling back to the empty string if nothing was stored.
                this.className = this.className || value === false ? "" : data_priv.get(this, "__className__") || "";
            }
        });
    },

    hasClass: function (selector) {
        var className = " " + selector + " ",
            i = 0,
            l = this.length;
        for (; i < l; i++) {
            if (this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf(className) >= 0) {
                return true;
            }
        }

        return false;
    },

    val: function (value) {
        var hooks, ret, isFunction,
            elem = this[0];

        if (!arguments.length) {
            if (elem) {
                hooks = jQuery.valHooks[elem.type] || jQuery.valHooks[elem.nodeName.toLowerCase()];

                if (hooks && "get" in hooks && (ret = hooks.get(elem, "value")) !== undefined) {
                    return ret;
                }

                ret = elem.value;

                return typeof ret === "string" ?
                    // handle most common string cases
                    ret.replace(rreturn, "") :
                    // handle cases where value is null/undef or number
                    ret == null ? "" : ret;
            }

            return;
        }

        isFunction = jQuery.isFunction(value);

        return this.each(function (i) {
            var val;

            if (this.nodeType !== 1) {
                return;
            }

            if (isFunction) {
                val = value.call(this, i, jQuery(this).val());
            } else {
                val = value;
            }

            // Treat null/undefined as ""; convert numbers to string
            if (val == null) {
                val = "";
            } else if (typeof val === "number") {
                val += "";
            } else if (jQuery.isArray(val)) {
                val = jQuery.map(val, function (value) {
                    return value == null ? "" : value + "";
                });
            }

            hooks = jQuery.valHooks[this.type] || jQuery.valHooks[this.nodeName.toLowerCase()];

            // If set returns undefined, fall back to normal setting
            if (!hooks || !("set" in hooks) || hooks.set(this, val, "value") === undefined) {
                this.value = val;
            }
        });
    }
});

jQuery.extend({
    valHooks: {
        option: {
            get: function (elem) {
                // attributes.value is undefined in Blackberry 4.7 but
                // uses .value. See #6932
                var val = elem.attributes.value;
                return !val || val.specified ? elem.value : elem.text;
            }
        },
        select: {
            get: function (elem) {
                var value, option,
                    options = elem.options,
                    index = elem.selectedIndex,
                    one = elem.type === "select-one" || index < 0,
                    values = one ? null : [],
                    max = one ? index + 1 : options.length,
                    i = index < 0 ?
                        max :
                        one ? index : 0;

                // Loop through all the selected options
                for (; i < max; i++) {
                    option = options[i];

                    // IE6-9 doesn't update selected after form reset (#2551)
                    if ((option.selected || i === index) &&
                        // Don't return options that are disabled or in a disabled optgroup
                        (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
                        (!option.parentNode.disabled || !jQuery.nodeName(option.parentNode, "optgroup"))) {

                        // Get the specific value for the option
                        value = jQuery(option).val();

                        // We don't need an array for one selects
                        if (one) {
                            return value;
                        }

                        // Multi-Selects return an array
                        values.push(value);
                    }
                }

                return values;
            },

            set: function (elem, value) {
                var optionSet, option,
                    options = elem.options,
                    values = jQuery.makeArray(value),
                    i = options.length;

                while (i--) {
                    option = options[i];
                    if ((option.selected = jQuery.inArray(jQuery(option).val(), values) >= 0)) {
                        optionSet = true;
                    }
                }

                // force browsers to behave consistently when non-matching value is set
                if (!optionSet) {
                    elem.selectedIndex = -1;
                }
                return values;
            }
        }
    },

    attr: function (elem, name, value) {
        var hooks, ret,
            nType = elem.nodeType;

        // don't get/set attributes on text, comment and attribute nodes
        if (!elem || nType === 3 || nType === 8 || nType === 2) {
            return;
        }

        // Fallback to prop when attributes are not supported
        if (typeof elem.getAttribute === core_strundefined) {
            return jQuery.prop(elem, name, value);
        }

        // All attributes are lowercase
        // Grab necessary hook if one is defined
        if (nType !== 1 || !jQuery.isXMLDoc(elem)) {
            name = name.toLowerCase();
            hooks = jQuery.attrHooks[name] ||
                (jQuery.expr.match.bool.test(name) ? boolHook : nodeHook);
        }

        if (value !== undefined) {

            if (value === null) {
                jQuery.removeAttr(elem, name);

            } else if (hooks && "set" in hooks && (ret = hooks.set(elem, value, name)) !== undefined) {
                return ret;

            } else {
                elem.setAttribute(name, value + "");
                return value;
            }

        } else if (hooks && "get" in hooks && (ret = hooks.get(elem, name)) !== null) {
            return ret;

        } else {
            ret = jQuery.find.attr(elem, name);

            // Non-existent attributes return null, we normalize to undefined
            return ret == null ?
                undefined :
                ret;
        }
    },

    removeAttr: function (elem, value) {
        var name, propName,
            i = 0,
            attrNames = value && value.match(core_rnotwhite);

        if (attrNames && elem.nodeType === 1) {
            while ((name = attrNames[i++])) {
                propName = jQuery.propFix[name] || name;

                // Boolean attributes get special treatment (#10870)
                if (jQuery.expr.match.bool.test(name)) {
                    // Set corresponding property to false
                    elem[propName] = false;
                }

                elem.removeAttribute(name);
            }
        }
    },

    attrHooks: {
        type: {
            set: function (elem, value) {
                if (!jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input")) {
                    // Setting the type on a radio button after the value resets the value in IE6-9
                    // Reset value to default in case type is set after value during creation
                    var val = elem.value;
                    elem.setAttribute("type", value);
                    if (val) {
                        elem.value = val;
                    }
                    return value;
                }
            }
        }
    },

    propFix: {
        "for": "htmlFor",
        "class": "className"
    },

    prop: function (elem, name, value) {
        var ret, hooks, notxml,
            nType = elem.nodeType;

        // don't get/set properties on text, comment and attribute nodes
        if (!elem || nType === 3 || nType === 8 || nType === 2) {
            return;
        }

        notxml = nType !== 1 || !jQuery.isXMLDoc(elem);

        if (notxml) {
            // Fix name and attach hooks
            name = jQuery.propFix[name] || name;
            hooks = jQuery.propHooks[name];
        }

        if (value !== undefined) {
            return hooks && "set" in hooks && (ret = hooks.set(elem, value, name)) !== undefined ?
                ret :
                (elem[name] = value);

        } else {
            return hooks && "get" in hooks && (ret = hooks.get(elem, name)) !== null ?
                ret :
                elem[name];
        }
    },

    propHooks: {
        tabIndex: {
            get: function (elem) {
                return elem.hasAttribute("tabindex") || rfocusable.test(elem.nodeName) || elem.href ?
                    elem.tabIndex :
                    -1;
            }
        }
    }
});

// Hooks for boolean attributes
boolHook = {
    set: function (elem, value, name) {
        if (value === false) {
            // Remove boolean attributes when set to false
            jQuery.removeAttr(elem, name);
        } else {
            elem.setAttribute(name, name);
        }
        return name;
    }
};
jQuery.each(jQuery.expr.match.bool.source.match(/\w+/g), function (i, name) {
    var getter = jQuery.expr.attrHandle[name] || jQuery.find.attr;

    jQuery.expr.attrHandle[name] = function (elem, name, isXML) {
        var fn = jQuery.expr.attrHandle[name],
            ret = isXML ?
                undefined :
                /* jshint eqeqeq: false */
                // Temporarily disable this handler to check existence
                (jQuery.expr.attrHandle[name] = undefined) !=
                    getter(elem, name, isXML) ?

                    name.toLowerCase() :
                    null;

        // Restore handler
        jQuery.expr.attrHandle[name] = fn;

        return ret;
    };
});

// Support: IE9+
// Selectedness for an option in an optgroup can be inaccurate
if (!jQuery.support.optSelected) {
    jQuery.propHooks.selected = {
        get: function (elem) {
            var parent = elem.parentNode;
            if (parent && parent.parentNode) {
                parent.parentNode.selectedIndex;
            }
            return null;
        }
    };
}

jQuery.each([
    "tabIndex",
    "readOnly",
    "maxLength",
    "cellSpacing",
    "cellPadding",
    "rowSpan",
    "colSpan",
    "useMap",
    "frameBorder",
    "contentEditable"
], function () {
    jQuery.propFix[this.toLowerCase()] = this;
});

// Radios and checkboxes getter/setter
jQuery.each(["radio", "checkbox"], function () {
    jQuery.valHooks[this] = {
        set: function (elem, value) {
            if (jQuery.isArray(value)) {
                return (elem.checked = jQuery.inArray(jQuery(elem).val(), value) >= 0);
            }
        }
    };
    if (!jQuery.support.checkOn) {
        jQuery.valHooks[this].get = function (elem) {
            // Support: Webkit
            // "" is returned instead of "on" if a value isn't specified
            return elem.getAttribute("value") === null ? "on" : elem.value;
        };
    }
});